package craky.keeper.client;

import java.util.Timer;
import java.util.TimerTask;

import android.content.res.Resources;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.RotateAnimation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import craky.keeper.util.KeeperUtil;
import craky.keeper.view.KeeperListView;
import craky.keeper.view.ListItem;

public class PullDownListener implements View.OnTouchListener
{
    private final DecelerateInterpolator DECELERATE_INTERPOLATOR = new MoveHeaderInterpolator();

    private final long PRESSABLE_TIMEOUT = ViewConfiguration.getTapTimeout() + 10;

    private final int PULL_DEVIATION = 3;

    private KeeperActivity activity;

    private View pullHeader;

    private LinearLayout listParent;

    private TextView headerText;

    private ImageView headerResultImage, headerStatusImage, headerLoadingImage;

    private ListItem currentItem;

    private Resources resources;

    private Timer delayHideTimer;

    private TimerTask delayHideTimerTask;

    private int headerHeight;

    private float lastRawY, currentTranslateValue;

    private boolean needRefresh, refreshing, pullForCancel, pullStarted, pullable, itemClickable;

    public PullDownListener(KeeperActivity activity)
    {
        this.activity = activity;
        this.pullable = true;
        this.itemClickable = true;
        this.listParent = (LinearLayout)activity.findViewById(R.id.listParent);
        this.pullHeader = (View)activity.findViewById(R.id.pullHeader);
        this.headerText = (TextView)activity.findViewById(R.id.headerText);
        this.headerResultImage = (ImageView)activity.findViewById(R.id.headerResultImage);
        this.headerStatusImage = (ImageView)activity.findViewById(R.id.headerStatusImage);
        this.headerLoadingImage = (ImageView)activity.findViewById(R.id.headerLoadingImage);
        this.resources = activity.getResources();
        this.delayHideTimer = new Timer(true);
        pullHeader.post(new Runnable()
        {
            public void run()
            {
                headerHeight = pullHeader.getHeight();
            }
        });
    }

    @Override
    public boolean onTouch(View view, MotionEvent event)
    {
        KeeperListView listView = (view instanceof KeeperListView)? (KeeperListView)view: null;
        boolean isEmptyViewTouched = listView == null;
        boolean consumed = isEmptyViewTouched;
        int action = event.getAction();

        switch(action)
        {
            case MotionEvent.ACTION_DOWN:
            {
                itemClickable = true;
                pullStarted = false;
                lastRawY = event.getRawY();

                if(listView != null)
                {
                    int position = listView.pointToPosition((int)event.getX(), (int)event.getY());
                    View child = listView.getChildAt(position);

                    if(child instanceof ListItem)
                    {
                        currentItem = (ListItem)child;
                    }
                }

                break;
            }
            case MotionEvent.ACTION_UP:
            {
                if(pullStarted)
                {
                    setPressable(true, listView, itemClickable? 0: PRESSABLE_TIMEOUT);
                    pullFinished();
                }

                lastRawY = 0;
                pullStarted = false;
                currentItem = null;
                break;
            }
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
            {
                lastRawY = 0;
                break;
            }
            case MotionEvent.ACTION_MOVE:
            {
                if(pullable)
                {
                    consumed = pullMove(listView, event) || isEmptyViewTouched;
                }

                break;
            }
        }

        return consumed;
    }

    private boolean pullMove(KeeperListView listView, MotionEvent event)
    {
        boolean consumed = false;
        boolean canPull = listView == null || listView.getChildAt(0).getY() >= 0;

        if(!pullStarted && canPull)
        {
            pullStart(event);
        }

        if(pullStarted && canPull)
        {
            if(lastRawY == 0)
            {
                lastRawY = event.getRawY();
            }
            else
            {
                float newRawY = event.getRawY();
                float deltaY = newRawY - lastRawY;
                float translationY = listParent.getTranslationY();
                lastRawY = newRawY;
                consumed = translationY > 0;

                if(consumed)
                {
                    setPressed(false, listView);
                }

                if(deltaY > 0)
                {
                    if(translationY < listParent.getHeight() / 2.0f)
                    {
                        float factor = 0.1f * ((translationY / 100) + 1);
                        deltaY = deltaY * (0.45f - Math.min(factor, 0.3f));
                    }
                    else
                    {
                        deltaY = deltaY * 0.3f;
                    }
                }
                else
                {
                    deltaY = deltaY * 0.5f;
                }

                float newTranslationY = deltaY + translationY;

                if(newTranslationY >= 0)
                {
                    listParent.setTranslationY(newTranslationY);
                    boolean newNeedRefresh = newTranslationY > headerHeight;

                    if(newTranslationY > 0)
                    {
                        setPressable(false, listView, 0);
                    }

                    if(newNeedRefresh != needRefresh)
                    {
                        needRefresh = newNeedRefresh;

                        if(!pullForCancel)
                        {
                            switchHeaderContentWhenPull(false);
                        }
                    }
                }
                else if(translationY > 0)
                {
                    listParent.setTranslationY(0);
                }
            }

            if(itemClickable && listParent.getTranslationY() >= PULL_DEVIATION)
            {
                itemClickable = false;
            }
        }
        else
        {
            lastRawY = 0;
        }

        return consumed;
    }

    private void pullStart(MotionEvent event)
    {
        pullStarted = true;
        needRefresh = false;
        pullForCancel = refreshing;

        if(delayHideTimerTask != null)
        {
            delayHideTimerTask.cancel();
            delayHideTimerTask = null;
        }

        if(!pullForCancel)
        {
            Animation animation = listParent.getAnimation();

            if(animation != null && animation.hasStarted() && !animation.hasEnded())
            {
                listParent.clearAnimation();
                listParent.setTranslationY(headerHeight * currentTranslateValue);
            }

            switchHeaderContentWhenPull(true);
        }
        else
        {
            listParent.clearAnimation();
            pullHeader.setVisibility(View.VISIBLE);
        }
    }

    private void pullFinished()
    {
        if(!needRefresh)
        {
            activity.getCurrentList().cancelLoad(false);

            if(listParent.getTranslationY() > 0)
            {
                moveHeader(0);
            }
            else
            {
                listParent.setTranslationY(0);
                listParent.clearAnimation();
                pullHeader.setVisibility(View.INVISIBLE);
            }
        }
        else
        {
            if(pullForCancel)
            {
                if(refreshing)
                {
                    activity.getCurrentList().cancelLoad(false);
                }

                moveHeader(0);
            }
            else
            {
                moveHeader(headerHeight);
                headerText.setText(resources.getString(R.string.refreshing));
                headerStatusImage.clearAnimation();
                headerStatusImage.setVisibility(View.INVISIBLE);
                headerLoadingImage.setVisibility(View.VISIBLE);
                refresh();
            }
        }

        needRefresh = false;
        pullForCancel = false;
    }

    private void refresh()
    {
        KeeperListView currentList = activity.getCurrentList();

        if(!currentList.isFirstFinished())
        {
            currentList.cancelLoad(false);
        }

        refreshing = true;
        float pivot = headerLoadingImage.getWidth() / 2.0f;
        Animation animation = new RotateAnimation(0, Integer.MAX_VALUE / 3.0f, pivot, pivot);
        animation.setInterpolator(KeeperUtil.LINEAR_INTERPOLATOR);
        animation.setDuration(Integer.MAX_VALUE);
        headerLoadingImage.startAnimation(animation);
        activity.getCurrentList().loadDatas();
    }

    public void beforeRefreshFinished()
    {
        lastRawY = 0;
        refreshing = false;
        headerLoadingImage.clearAnimation();
    }

    public void refreshFinished(String errorMessage, boolean cancel)
    {
        beforeRefreshFinished();

        if(pullHeader.getVisibility() == View.VISIBLE)
        {
            if(cancel && !pullForCancel)
            {
                listParent.setTranslationY(0);
                listParent.clearAnimation();
                pullHeader.setVisibility(View.INVISIBLE);
            }
            else if(!cancel)
            {
                if(errorMessage == null)
                {
                    headerResultImage.setImageDrawable(resources.getDrawable(R.drawable.success));
                    headerText.setText(resources.getString(R.string.refresh_success));
                }
                else
                {
                    headerResultImage.setImageDrawable(resources.getDrawable(R.drawable.failed));
                    headerText.setText(errorMessage);
                }

                headerLoadingImage.setVisibility(View.INVISIBLE);
                headerResultImage.setVisibility(View.VISIBLE);

                if(!pullForCancel)
                {
                    delayHideTimer.schedule(delayHideTimerTask = new TimerTask()
                    {
                        public void run()
                        {
                            pullHeader.post(new Runnable()
                            {
                                public void run()
                                {
                                    moveHeader(0);
                                    delayHideTimerTask = null;
                                }
                            });
                        }
                    }, 750);
                }
            }
        }
    }

    private void switchHeaderContentWhenPull(boolean resetViesVisibility)
    {
        if(resetViesVisibility)
        {
            headerStatusImage.setRotation(needRefresh? 180: 0);
            headerStatusImage.clearAnimation();
            headerLoadingImage.clearAnimation();
            headerResultImage.setVisibility(View.INVISIBLE);
            headerLoadingImage.setVisibility(View.INVISIBLE);
            headerText.setVisibility(View.VISIBLE);
            headerStatusImage.setVisibility(View.VISIBLE);
            pullHeader.setVisibility(View.VISIBLE);
        }

        if(needRefresh)
        {
            headerText.setText(resources.getString(R.string.release_to_refresh));

            if(!resetViesVisibility)
            {
                startRotation(0, 180);
            }
        }
        else
        {
            headerText.setText(resources.getString(R.string.pull_down_refresh));

            if(!resetViesVisibility)
            {
                startRotation(180, 0);
            }
        }
    }

    private void startRotation(float fromDegrees, float toDegrees)
    {
        headerStatusImage.clearAnimation();
        float pivotX = headerStatusImage.getWidth() / 2.0f;
        float pivotY = headerStatusImage.getHeight() / 2.0f;
        Animation animation = new RotateAnimation(fromDegrees - toDegrees, 0, pivotX, pivotY);
        animation.setInterpolator(KeeperUtil.LINEAR_INTERPOLATOR);
        animation.setDuration(100);
        headerStatusImage.setRotation(toDegrees);
        headerStatusImage.startAnimation(animation);
    }

    private void moveHeader(float toY)
    {
        listParent.clearAnimation();

        if(listParent.getVisibility() == View.VISIBLE)
        {
            float deltaY = listParent.getTranslationY() - toY;
            Animation animation = new TranslateAnimation(0.0f, 0.0f, deltaY, 0);
            animation.setDuration(200);
            animation.setInterpolator(DECELERATE_INTERPOLATOR);
            listParent.setTranslationY(toY);

            if(toY == 0)
            {
                animation.setAnimationListener(new Animation.AnimationListener()
                {
                    public void onAnimationStart(Animation animation)
                    {}

                    public void onAnimationRepeat(Animation animation)
                    {}

                    public void onAnimationEnd(Animation animation)
                    {
                        if(!pullStarted)
                        {
                            listParent.setTranslationY(0);
                            pullHeader.setVisibility(View.INVISIBLE);
                        }
                    }
                });
            }

            listParent.startAnimation(animation);
        }
    }

    public void resetHeader()
    {
        listParent.clearAnimation();
        listParent.setTranslationY(0);
        pullHeader.setVisibility(View.INVISIBLE);
        headerLoadingImage.clearAnimation();
        headerStatusImage.clearAnimation();
    }

    private void setPressable(final boolean pressable, final KeeperListView listView, long delayMillis)
    {
        if(listView != null && currentItem != null)
        {
            if(pressable && delayMillis > 0)
            {
                listView.postDelayed(new Runnable()
                {
                    public void run()
                    {
                        listView.setPressable(pressable);
                    }
                }, delayMillis);
            }
            else
            {
                listView.setPressable(pressable);
            }
        }
    }

    private void setPressed(boolean pressed, KeeperListView listView)
    {
        if(listView != null && currentItem != null)
        {
            currentItem.setPressed(pressed);
            listView.setPressed(pressed);
        }
    }

    public boolean isLongClickable()
    {
        return !refreshing && listParent.getTranslationY() < PULL_DEVIATION;
    }

    public boolean isPullable()
    {
        return this.pullable;
    }

    public void setPullable(boolean pullable)
    {
        this.pullable = pullable;
    }

    public boolean isItemClickable()
    {
        return this.itemClickable;
    }

    private class MoveHeaderInterpolator extends DecelerateInterpolator
    {
        public MoveHeaderInterpolator()
        {
            super();
        }

        public float getInterpolation(float input)
        {
            currentTranslateValue = super.getInterpolation(input);
            return currentTranslateValue;
        }
    }
}